#ifndef META_HPP
#define META_HPP

/// ref: https://zhuanlan.zhihu.com/p/87917516

#include <iostream>
#include <string>
#include <type_traits>

namespace meta {

template <unsigned Val> struct _isZero { constexpr static bool value = false; };

template <> struct _isZero<0> { constexpr static bool value = true; };

template <unsigned Val> constexpr bool isZero = _isZero<Val>::value;

void test1() {
  static_assert(!isZero<1>, "compile error");
  static_assert(isZero<0>, "compile error");
}

template <typename T> constexpr bool isNum = std::is_arithmetic<T>::value;

template <typename T>
constexpr bool isStr = std::is_same<T, const char *>::value;

template <typename T> constexpr bool isBad = !isNum<T> && !isStr<T>;

template <typename T> std::enable_if_t<isNum<T>, std::string> ToString(T num) {
  return std::to_string(num);
}

template <typename T> std::enable_if_t<isStr<T>, std::string> ToString(T str) {
  return std::string(str);
}

template <typename T> std::enable_if_t<isBad<T>, std::string> ToString(T bad) {
  static_assert(sizeof(T) == 0, "neither Num nor Str");
}

void test2() {
  auto a = ToString(1);     // std::to_string (num);
  auto b = ToString(1.0);   // std::to_string (num);
  auto c = ToString("0x0"); // std::string (str);
  // auto d = ToString(std::string{}); // not compile :-(
}

template <unsigned N> constexpr unsigned _Factor() {
  return N * _Factor<N - 1>();
}

template <> constexpr unsigned _Factor<0>() { return 1; }

template <unsigned N> constexpr unsigned Factor = _Factor<N>();

void test3() {
  static_assert(Factor<0> == 1, "compile error");
  static_assert(Factor<1> == 1, "compile error");
  static_assert(Factor<4> == 24, "compile error");
}

void test4() {
  int a = 4;
  int arr[a];

  const int b = 4;
  // const int arr2[b];

  constexpr int c = 5;
  const int arr3[b] = {1, 2, 3};
}

template <class _Tp, _Tp __v> struct my_integral_constant {
  static constexpr const _Tp value = __v;
  typedef _Tp value_type;
  typedef my_integral_constant type;

  _LIBCPP_CONSTEXPR operator value_type() const noexcept {
    std::cout << __FUNCTION__ << std::endl;
    return value;
  }

#if _LIBCPP_STD_VER > 11
  constexpr value_type operator()() const noexcept {
    std::cout << __FUNCTION__ << std::endl;
    return value;
  }
#endif
};

void test5() {
  constexpr my_integral_constant<int, 0> a;
  const int ret1 = a();
  std::cout << ret1 << std::endl;
  const int ret2 = a.operator int();
  std::cout << ret2 << std::endl;
  const int b = 3;
  // const int ret3 = 3();
  // std::cout << ret3 << std::endl;
}

// ref: https://zh.cppreference.com/w/cpp/language/cv

class ThreadsafeCounter {
  mutable std::mutex m; // “M&M 规则”：mutable 与 mutex 一起出现
  int data = 0;
  mutable int data2 = 1;

public:
  int get() const {
    std::lock_guard<std::mutex> lk(m);
    return data;
  }
  void inc() {
    std::lock_guard<std::mutex> lk(m);
    ++data;
  }
  void inc2() const { ++data2; }
};

void test6() {
  const ThreadsafeCounter o;
  int ret = o.get();
}

class ObjectSize {
private:
  std::int32_t p_size;

public:
  std::int32_t size;

public:
  virtual void print() = 0;
  virtual void print2(std::int32_t v) = 0;

public:
  ObjectSize(/* args */) : size(0), p_size(0) {}
  ~ObjectSize() {}
};

class DerivedClass : public ObjectSize {
public:
  std::int32_t sub_size;

public:
  DerivedClass(/* args */) : ObjectSize(), sub_size() {}
  ~DerivedClass() {}
  virtual void print() override { std::cout << __func__ << std::endl; }
  virtual void print2(std::int32_t v) override {
    std::cout << __func__ << v << std::endl;
  }
};

/// 虚函数表占类本身大小
void test7() {
  std::cout << sizeof(ObjectSize) << std::endl;
  // ObjectSize o;
  // std::cout << sizeof(o) << std::endl;
  std::cout << sizeof(DerivedClass) << std::endl;
  DerivedClass d;
  std::cout << sizeof(d) << std::endl;
}

template <std::size_t N> struct MyAllocator {
  char data[N];
  void *p;
  std::size_t sz;
  MyAllocator() : p(data), sz(N) {}
  template <typename T> T *aligned_alloc(std::size_t a = alignof(T)) {
    std::cout << "alignof(T) = " << a << std::endl;
    if (std::align(a, sizeof(T), p, sz)) {
      T *result = reinterpret_cast<T *>(p);
      p = (char *)p + sizeof(T);
      sz -= sizeof(T);
      return result;
    }
    return nullptr;
  }
};

void test8() {
  MyAllocator<64> a;
  std::cout << "allocated a char at " << (void *)a.p << '\n';

  // 分配一个 char
  char *p1 = a.aligned_alloc<char>();
  if (p1)
    *p1 = 'a';
  std::cout << "allocated a char at " << (void *)p1 << '\n';

  // 分配一个 int
  int *p2 = a.aligned_alloc<int>();
  if (p2)
    *p2 = 1;
  std::cout << "allocated an int at " << (void *)p2 << '\n';

  // 分配一个 int ，对齐于 32 字节边界
  int *p3 = a.aligned_alloc<int>(32);
  if (p3)
    *p3 = 2;
  std::cout << "allocated an int at " << (void *)p3 << " (32 byte alignment)\n";
}

// S 类型的对象可以在任何地址上分配
// 因为 S.a 和 S.b 都可以在任何地址上分配
struct S {
  char a; // 大小：1，对齐：1
  char b; // 大小：1，对齐：1
};        // 大小：2，对齐：1

// X 类型的对象只能在 4 字节边界上分配
// 因为 X.n 必须在 4 字节边界上分配
// 因为 int 的对齐要求（通常）就是 4
struct X {
  int n;  // 大小：4，对齐：4
  char c; // 大小：1，对齐：1
  // 三个填充字节
}; // 大小：8，对齐：4

struct A1 {
  char c; // 大小：1，对齐：1
  // 三个填充字节
  int n; // 大小：4，对齐：4
};       // 大小：8，对齐：4

struct A2 {
  char c; // 大小：1，对齐：1
  // 1个填充字节
  std::int16_t d; // 大小：2，对齐：2
  int n;          // 大小：4，对齐：4
};                // 大小：8，对齐：4

struct A3 {
  char c; // 大小：1，对齐：1
  // 1个填充字节
  std::int16_t d; // 大小：2，对齐：2
  int n;          // 大小：4，对齐：4
  long n1;
  long long n2;
}; // 大小：8，对齐：4

void test9() {
  std::cout << "sizeof(S) = " << sizeof(S) << " alignof(S) = " << alignof(S)
            << '\n';
  std::cout << "sizeof(X) = " << sizeof(X) << " alignof(X) = " << alignof(X)
            << '\n';
  std::cout << "sizeof(A1) = " << sizeof(A1) << " alignof(A1) = " << alignof(A1)
            << '\n';
  std::cout << "sizeof(A2) = " << sizeof(A2) << " alignof(A2) = " << alignof(A2)
            << '\n';
  std::cout << "sizeof(A3) = " << sizeof(A3) << " alignof(A3) = " << alignof(A3)
            << '\n';

  A3 *p = new A3;
  p->d = 3;
  std::memset(p, 1, sizeof(A3));
  std::cout << (char)*(char *)p << std::endl;
  std::cout << (short)*(char *)(p + 1) << std::endl;
  std::cout << (short)*(char *)(p + 2) << std::endl;
}

struct bit_field {
  std::int32_t v1 : 1;
  std::int32_t v2 : 2;
  std::int32_t v3 : 2;
};

void test10() {
  bit_field bf;
  bf.v1 = 1;
  bf.v2 = 0;
  bf.v3 = -2;
  // bf.v2 = 2;
  std::cout << "bf.v1" << std::endl;
  std::cout << bf.v1 << std::endl;
  std::cout << bf.v2 << std::endl;
  std::cout << bf.v3 << std::endl;
  std::cout << sizeof(bit_field) << std::endl;
  std::cout << alignof(bit_field) << std::endl;
}

void test11() {
  std::cout << "first" << std::endl;
  if (false) {
    goto haha;
  }
  std::cout << "second" << std::endl;
haha:
  std::cout << "third" << std::endl;
}

void test12() {
  void *p = new int(2);
  void **pp = &p;
  void *r = *pp;
  int rr = *(int *)r;
  int s = *(int *)*pp;
  std::cout << rr << std::endl;
  // 如果你像我一样不会写出这个样的表达式，那么不如我们想上面一样先写分布表达式i，在写最终表达式
  std::cout << *(int *)*pp << std::endl;
}

} // namespace meta

// This C++ function can be called from C code
// c代码里面可以调用c++代码
extern "C" void handler(int) {
  std::cout << "Callback invoked\n"; // It can use C++
}

void meta_demo() { meta::test11(); }

#endif // META_HPP